// examples/chat/src/main.rs
// examples/file-sharing/src/network.rs

use libp2p::{core::Multiaddr, identity, kad, noise, swarm::Swarm, tcp, yamux};
use tokio::{sync::mpsc, time::Duration};
use tracing::info;

use crate::network_api::{P2PCommandOfApp, P2PEventForApp};
use crate::network_core::P2pNetWorkError;
use crate::network_core::network_core_impl::{
    combined_behaviour::CombinedBehaviour, network_manager::NetworkManager,
};
use crate::network_core::network_core_inner_api::NetWorkCoreInnerApi;

// impl From<libp2p::TransportError<Box<dyn std::error::Error + Send + Sync>>> for P2pNetWorkError {
//     fn from(err: libp2p::TransportError<Box<dyn std::error::Error + Send + Sync>>) -> Self {
//         Self::SwarmTransportError(format!("{:?}", err).into())
//     }
// }

pub(crate) mod combined_behaviour;
pub(crate) mod config;
pub(crate) mod network_manager;

/// Creates the network components, namely:
/// - The network client to interact with the network layer from anywhere within your application.
/// - The network event stream, e.g. for incoming requests.
/// - The network task driving the network itself.
pub(crate) async fn new_network_core(
    secret_key_seed: Option<u8>,
    listen_address: Option<Multiaddr>,
) -> Result<
    (
        NetWorkCoreInnerApi,
        mpsc::Sender<P2PCommandOfApp>,
        mpsc::Receiver<P2PEventForApp>,
        NetworkManager,
    ),
    P2pNetWorkError,
> {
    let swarm = build_swarm(secret_key_seed, listen_address)?;
    let (command_sender_for_network_core, command_receiver_for_network_core) = mpsc::channel(8);
    let (command_sender_for_app, command_receiver_for_app) = mpsc::channel(32);

    let (event_sender_for_app, event_receiver_for_app) = mpsc::channel(32);

    info!("Build swarm done for node({}).", swarm.local_peer_id());

    Ok((
        NetWorkCoreInnerApi {
            command_sender_for_network_core,
        },
        command_sender_for_app,
        event_receiver_for_app,
        NetworkManager::new(
            swarm,
            command_receiver_for_network_core,
            command_receiver_for_app,
            event_sender_for_app,
        ),
    ))
}

// swarm：网络行为管理器，协调各种网络协议
fn build_swarm(
    secret_key_seed: Option<u8>,
    listen_address: Option<Multiaddr>,
) -> Result<Swarm<CombinedBehaviour>, P2pNetWorkError> {
    // Create a public/private key pair, either random or based on a seed.
    let id_keys = match secret_key_seed {
        Some(seed) => {
            let mut bytes = [0u8; 32];
            bytes[0] = seed;
            identity::Keypair::ed25519_from_bytes(bytes).unwrap()
        }
        None => identity::Keypair::generate_ed25519(),
    };
    // let peer_id = id_keys.public().to_peer_id();

    let mut swarm = libp2p::SwarmBuilder::with_existing_identity(id_keys)
        .with_tokio()
        .with_tcp(
            tcp::Config::default(),
            noise::Config::new,
            yamux::Config::default,
        )?
        .with_behaviour(CombinedBehaviour::new)?
        .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60)))
        .build();

    // swarm
    //     .behaviour_mut()
    //     .gossipsub_subscribe(crate::BC_FLOOD_TOPIC_NAME_PREFIX)?;

    match listen_address {
        Some(addr) => swarm.listen_on(addr)?,
        None => {
            // Listen on all interfaces and whatever port the OS assigns
            // swarm.listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse()?)?;
            // swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
            swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?
        }
    };

    println!("[{}] Node started", swarm.local_peer_id()); // MARK, depended by test_p2p_discovery.py

    swarm
        .behaviour_mut()
        .set_kademlia_mode(Some(kad::Mode::Server));

    Ok(swarm)
}
